#include <xc.h>
#include <string.h>
#include <stdio.h>
#include "../src/SPI.h"
#include "../src/SPI_FrameWork.h"
#include "../src/Framework.h"
#include "../src/Debug.h"
#include "../src/config/pic32mk_mcm_curiosity_pro/peripheral/gpio/plib_gpio.h"
#include "Uart5.h"
#include "PanelComm.h"

NEW_SPI_MSG_STRUCT  New_SPI_MSG;
SPI_WORK_MSG_STRUCT  SPI_WorkMsg;
SSS_MSG_STRUCT    SPI_Msg;

static unsigned char SPI_TxBuf[WRITE_DATA_SIZE + 3];   // Head + CRC 2
void Uart_TxByte(unsigned char txChar);
extern void PutChar(uint8_t txChar);
volatile static bool isTransferDone = false;

void SlaveDeselect(uint8_t slaveNum);

const uint8_t SPI_HEADER_FOR_SLAVE[9] = {0, 0, 0xA2, 0xA1, 0xA3, 0xA4, 0xA7, 0xA6, 0xA6};
const uint8_t SPI_NUM_FOR_SLAVE[9] = {0, 0, 2, 5, 3, 4, 6, 4, 0};

uint8_t GetSPI_TransmitFrame(uint8_t* Buff)
{
	WORD_VAL  crc;
    uint8_t     BuffLen = 0;
    uint8_t     i;
    
    // New_SPI_MSG.Len < 2, frame error (at least 2 bytes above, CMD + len +...)
    // New_SPI_MSG.Len > (WRITE_DATA_SIZE - 2),after adding a two-byte CRC, New_SPI_MSG.WData[] overflows and the frame fails
    if( (New_SPI_MSG.Len < 2) || (New_SPI_MSG.Len > (WRITE_DATA_SIZE - 2)) )  
        return 0xFF;
        
    // Calculate CRC of the frame.
    crc.Val = Crc_Fun(New_SPI_MSG.WData, (uint32_t)New_SPI_MSG.Len, 0);
    New_SPI_MSG.WData[New_SPI_MSG.Len++] = crc.byte.LB;
    New_SPI_MSG.WData[New_SPI_MSG.Len++] = crc.byte.HB; 	

    // Insert SOH (Indicates beginning of the frame)	
    Buff[BuffLen++] = Framehead_SPI;

    // Insert Data Link Escape Character.
    for(i = 0; i < New_SPI_MSG.Len; i++)
    {
        Buff[BuffLen++] = New_SPI_MSG.WData[i];
    } 

    New_SPI_MSG.Len = 0; // Purge this buffer, no more required.
    
    return(New_SPI_MSG.WData[1]); // Return buffer length. 
}


void Delay_290us(void) // 14.5us @FCY=40MHZ
{
    unsigned int temp;
    for( temp = 0; temp < 1400; temp++ )  //  103 @FCY=60MHZ
        ;
}

uint16_t SPI_WriteExchange(uint8_t slaveNum, uint16_t data)
{
    uint8_t whichSpi = SPI_NUM_FOR_SLAVE[slaveNum];
    
    if (whichSpi == 2)//X-axis
    {
        return WriteExchange_SPI2(data);
    }
    else if (whichSpi == 3)//Z-axis
    {
        return WriteExchange_SPI3(data);
    }
    else if (whichSpi == 4)//Cut-axis
    {
        return WriteExchange_SPI4(data);
    }
    else if (whichSpi == 5)//Y-axis
    {
        return WriteExchange_SPI5(data);
    }
    else //Holtek(reserved)
    {
        return WriteExchange_SPI6(data);
    }
}

void QIXIN_EnterBoot(uint8_t slaveNum)   
{
    uint8_t buf[12];
    uint8_t i;
    
    buf[0] = SPI_HEADER_FOR_SLAVE[slaveNum];
    buf[1]=0x00;
    buf[2]=0x51;
    buf[3]=0x00;
    buf[4]=0x00;
    buf[5]=0x00;
    buf[6]=0x00;
    buf[7]=0x00;
    buf[8]=0x00;
    buf[9]=0x00;
 
    uint16_t crc16 = CRC16_Chk(buf,10);
    buf[10] = crc16 >> 8;
    buf[11] = crc16;

    SlaveSelect(slaveNum);
    for (i = 0; i < 12; i++) 
    {
        SPI_WriteExchange(slaveNum, buf[i]);
    }  
    SlaveDeselect(slaveNum);
}

void SPI_WriteInstruction(uint8_t slaveNum, NEW_SPI_MSG_STRUCT*  pDat)   // Send one byte + interval = 13us about
{
    uint8_t i;
    uint8_t data_len;
    uint8_t *ptr;
    
    SlaveSelect(slaveNum);

    ptr = SPI_TxBuf;
    data_len = GetSPI_TransmitFrame(ptr);
    
    if(data_len == 0xFF )              // Frame error
        return;
    
    SPI_WriteExchange(slaveNum, *(ptr++));  // head

    data_len = data_len + 4;           // + cmd + len + data_len(itself) + crc 2bytes
    
    for(i = 0; i < data_len; i++)
    {
        SPI_WriteExchange(slaveNum, *(ptr++));
        
        if(i == 1)
        {
            int j;
            for (j = 0; j < 250; j++)
            {
                asm("nop");
            }
//            Delay_5uS(); 
//            Delay_5uS(); 
//            Delay_5uS(); 
        }
    } 
    SlaveDeselect(slaveNum);

    //******************************
    //PutChar(SPI_TxBuf[0]);
    //for(i=0;i<data_len;i++)
    //PutChar(SPI_TxBuf[i+1]);
    //******************************
    
}


/********************************************************************
* Function: 	uint8_t SPI_ReadInstruction(NEW_SPI_MSG_STRUCT*  pDat)
* Precondition: 
* Input: 		NEW_SPI_MSG_STRUCT*  pDat.
* Output:		NEW_SPI_MSG_STRUCT*  pDat..
* Return::	    uint8_t.	
*               0x01: Frame error
* Note:		 	None.
********************************************************************/
uint8_t SPI_ReadInstruction(uint8_t slaveNum, NEW_SPI_MSG_STRUCT* pDat)    
{
    uint8_t  i;
    uint8_t  data_len;
    uint8_t  *ptr;
    WORD_VAL crc;
    WORD_VAL crc22;
    uint8_t  crc_buf[READ_DATA_SIZE];
    
    SlaveSelect(slaveNum);

    ptr = SPI_TxBuf; 
    data_len = GetSPI_TransmitFrame(ptr); 
    if(data_len == 0xFF )                   
        return 1;
    if(data_len > READ_DATA_SIZE )           // Prevent  Read pDat->RData[] overflow
        return 1; 

    SPI_WriteExchange(slaveNum, *(ptr++));

    crc_buf[0] = *ptr;

    SPI_WriteExchange(slaveNum, *(ptr++));

    crc_buf[1] = *ptr;

    SPI_WriteExchange(slaveNum, *(ptr++));

    int j;
    for (j = 0; j < 250; j++)
    {
        asm("nop");
    }
//    Delay_5uS(); 
//    Delay_5uS(); 
//    Delay_5uS(); 

    for(i=0;i<data_len;i++)            
    {
        pDat->RData[i] = SPI_WriteExchange(slaveNum, *(ptr++));

        crc_buf[i+2] = pDat->RData[i];     
    }


    crc.byte.LB = SPI_WriteExchange(slaveNum, *(ptr++));
    crc.byte.HB = SPI_WriteExchange(slaveNum, *(ptr++));

    crc22.Val = Crc_Fun(&crc_buf[0], (uint32_t)(data_len+2), 0); 
    SlaveDeselect(slaveNum); 
    
    if(crc.Val != crc22.Val)
    {
        return 2;  // ERROR
    }
    
    return 0;      // OK
}

void SPI_FrameWork_Init(void)
{
    unsigned char i;
    
    SPI_WorkMsg.Read_En = 0;
    SPI_WorkMsg.Len = 0;
    SPI_WorkMsg.DlayValue = 0;
    for(i=0;i<20;i++)
    {
        SPI_WorkMsg.Data[i] = 0;
    }
    SPI_WorkMsg.Cmd = 0;
    
    //******************************
    for(i=0;i<WRITE_DATA_SIZE;i++)
    {    
        New_SPI_MSG.WData[i] = 0;
    }
    for(i=0;i<READ_DATA_SIZE;i++)
    {    
        New_SPI_MSG.RData[i] = 0;
    }
    New_SPI_MSG.RW = SPI_CMD_Read;
    New_SPI_MSG.Len = 0;
    //******************************
}


void Uart_TxByte(unsigned char txChar)
{
//    while(U2STAHbits.UTXBF); // wait for TX buffer to be empty
    while((U4STA & _U4STA_UTXBF_MASK));
    U4TXREG = txChar;
}

void SlaveSelect(uint8_t slaveNum) {
    switch (slaveNum) {
        case 2: // X-axis
            SPI_SEL2_Clear();
            break;

        case 3: // Y-axis 
            SPI_SEL5_Clear();
            break;

        case 4: //Z-axis
            SPI_SEL3_Clear();
            break;

        case 5: //cut-axis
            SPI_SEL7_Clear();
            break;

        case 6: 
            SPI_SEL6_Clear();
            break;

        case 7: 
            SPI_SEL4_Clear();
            break;
        
        default:
            break;
    }
}

void SlaveDeselect(uint8_t slaveNum) {
    switch (slaveNum) {
        case 2:
            SPI_SEL2_Set();
            break;
            
        case 3:
            SPI_SEL5_Set();
            break;

        case 4:
            SPI_SEL3_Set();
            break;

        case 5:
            SPI_SEL7_Set();
            break;

        case 6: 
            SPI_SEL6_Set();
            break;

        case 7:
            SPI_SEL4_Set();
            break;

        default:
            break;
    }
}

void SPIEventHandler(uintptr_t context ) {
    isTransferDone = true;
    
    /* De-assert the CS line */
    SlaveDeselect(7);  //SPI6_SS_Set();
}
